package org.mentalsmash.tazio.evaluation;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.net.MalformedURLException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.management.AttributeNotFoundException;
import javax.management.InstanceNotFoundException;
import javax.management.IntrospectionException;
import javax.management.JMException;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanServerConnection;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.ReflectionException;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;

public class JMXDataRetriever {
    private final String serviceURL;

    private MBeanServerConnection server = null;

    private final String host;

    private final String testId;

    public JMXDataRetriever(String host, String testId, String serviceURL) {
	this.serviceURL = serviceURL;
	this.host = host;
	this.testId = testId;
	JMXServiceURL url = null;

	try {
	    url = new JMXServiceURL(serviceURL);
	} catch (MalformedURLException e) {
	    e.printStackTrace();

	    throw new IllegalArgumentException(
		    "Cannot connecto to supplied url " + serviceURL);
	}

	JMXConnector connector = null;

	try {
	    connector = JMXConnectorFactory.connect(url);
	} catch (IOException e) {
	    e.printStackTrace();
	}

	try {
	    this.server = connector.getMBeanServerConnection();
	} catch (IOException e) {
	    e.printStackTrace();
	}

    }

    public JMXDataRetriever(String host, String testId) {
	this.serviceURL = "local";
	this.host = host;
	this.testId = testId;
	this.server = ManagementFactory.getPlatformMBeanServer();
    }

    public Map<String, Object> dumpMBean(ObjectName mbeanName) throws Exception {
	Map<String, Object> mbeanDump = new HashMap<String, Object>();

	MBeanInfo mbeanInfo = null;

	try {
	    mbeanInfo = this.server.getMBeanInfo(mbeanName);
	} catch (InstanceNotFoundException e) {
	    e.printStackTrace();
	} catch (IntrospectionException e) {
	    e.printStackTrace();
	} catch (ReflectionException e) {
	    e.printStackTrace();
	} catch (IOException e) {
	    e.printStackTrace();
	}

	if (mbeanInfo == null) {
	    throw new Exception("Cannot retrieve mbean's info : " + mbeanName
		    + " on " + this.serviceURL);
	}

	for (MBeanAttributeInfo attrInfo : mbeanInfo.getAttributes()) {
	    String attrName = attrInfo.getName();
	    Object value = null;
	    try {
		value = this.server.getAttribute(mbeanName, attrName);
	    } catch (AttributeNotFoundException e) {
		e.printStackTrace();
	    } catch (InstanceNotFoundException e) {
		e.printStackTrace();
	    } catch (MBeanException e) {
		e.printStackTrace();
	    } catch (ReflectionException e) {
		e.printStackTrace();
	    } catch (IOException e) {
		e.printStackTrace();
	    }

	    if (value != null) {
		mbeanDump.put(attrName, value);
	    }
	}

	return mbeanDump;
    }

    public ObjectName[] getObjectNames() {
	try {
	    ObjectName[] names = new ObjectName[] {
		    new ObjectName(
			    "org.mentalsmash.tazio.net:type=ServerFacade"),
		    new ObjectName("org.mentalsmash.tazio.net.ril:type="
			    + "RemoteInvocationDispatcher"),
		    new ObjectName("org.mentalsmash.tazio.net.ril:type="
			    + "RemoteInvocationMessageHandler"),
		    new ObjectName("org.mentalsmash.tazio.net.ril:type="
			    + "RemoteObjectsRegistry"),
		    new ObjectName("org.mentalsmash.tazio.cache:type=Cache"),
		    new ObjectName("org.mentalsmash.tazio.transactions:type="
			    + "TransactionManagerMonitor"),
		    new ObjectName("org.mentalsmash.tazio.transactions",
			    "type", "RMFacadeMonitor"),
		    new ObjectName("org.mentalsmash.tazio.transactions:type="
			    + "TransactionsLogMonitor"),
		    new ObjectName("org.mentalsmash.tazio.evaluation:type="
			    + "TestData") };

	    return names;
	} catch (Exception e) {
	    e.printStackTrace();
	    return null;
	}
    }

    public void dumpToFile(ObjectName mbeanName) throws Exception {
	Map<String, Object> values = this.dumpMBean(mbeanName);

	StringBuilder row = new StringBuilder();

	row.append(testId + "," + host + ",");

	Iterator<Object> it = values.values().iterator();

	while (it.hasNext()) {
	    Object curr = it.next();
	    row.append("\"" + curr + "\"");
	    if (it.hasNext()) {
		row.append(",");
	    }
	}

	this.appendToFile(mbeanName, values.keySet(), row.toString());

    }

    private void appendToFile(ObjectName name, Collection<String> keys,
	    String row) {

	String dir = "jmx";

	String fileName = dir + File.separatorChar
		+ name.getKeyProperty("type") + ".csv";
	// String fileName = name.getCanonicalName().replaceAll(":", "_")
	// .replaceAll(",", "_").replaceAll(" ", "") + ".csv";

	File file = new File(fileName);

	boolean exists = file.exists();

	BufferedWriter out = null;

	try {
	    out = new BufferedWriter(new FileWriter(file, true));

	    if (!exists) {
		StringBuilder header = new StringBuilder();
		Iterator<String> keysIt = keys.iterator();

		header.append("TestId,MBeanHost,");

		while (keysIt.hasNext()) {
		    String key = keysIt.next();
		    header.append(key);
		    if (keysIt.hasNext()) {
			header.append(",");
		    }
		}

		out.append(header.toString());
		out.newLine();
	    }

	    out.append(row);
	    out.newLine();
	    out.flush();
	} catch (IOException e) {
	    e.printStackTrace();
	} finally {
	    try {
		out.close();
	    } catch (IOException ex) {
		ex.printStackTrace();
	    }
	}

    }

    public void dump() throws Exception {
	waitThemToBeReady();
	for (ObjectName name : this.getObjectNames()) {
	    try {
		this.dumpToFile(name);
	    } catch (Exception e) {
		e.printStackTrace();
	    }
	}
    }
    
    private void waitThemToBeReady() {
	ObjectName pollMbean = null;
	try {
	    pollMbean = new ObjectName("org.mentalsmash.tazio.evaluation:type="
	    	    + "TestData");
	} catch (MalformedObjectNameException e1) {
	    e1.printStackTrace();
	} catch (NullPointerException e1) {
	    e1.printStackTrace();
	}
	
	boolean finished = false;
	while(!finished) {
	    try {
		Boolean b = (Boolean) server.getAttribute(pollMbean, "Finished");
		if(b != null) finished = b;
		try {
		    Thread.sleep(50);
		} catch (InterruptedException e) {
		    e.printStackTrace();
		}
	    } catch (IOException e) {
		e.printStackTrace();
	    } catch (JMException e) {
		e.printStackTrace();
	    } 
	}
    }

}
